package com.marsdl.idiom.service;

import com.alibaba.fastjson.JSON;
import com.marsdl.idiom.dto.IdiomOutputDTO;
import com.marsdl.idiom.dto.WebResult;
import com.marsdl.idiom.entity.IdiomEntity;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.util.IOUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.context.request.async.DeferredResult;

import javax.annotation.PostConstruct;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 新版成语接龙的类
 *  新版的成语接龙方法均在这一个类中
 *
 *  @author chenrui
 *  @since 2019-04-25
 *
 */
@Service
public class IdiomChain   {
    private Logger log = LogManager.getLogger(IdiomChain.class);

    private static Map<String, List<String>> idiomMap;
    private static List<String> idiomWordsList;
    private static ConcurrentHashMap<String, List<IdiomEntity>> uuidIdiomMap = new ConcurrentHashMap<>();
    private static Map<String, List<String>> pyingTree = new HashMap<>();

    /**
     * 加载所有成语进入hashmap集合中
     */
    @PostConstruct
    public void initIdiom()  {
        ClassPathResource classPathResource = new ClassPathResource("idiom.json");
        try {
            String str = IOUtils.toString(new InputStreamReader(classPathResource.getInputStream(), StandardCharsets.UTF_8));
            idiomMap = JSON.parseObject(str.toString(), Map.class);
            idiomWordsList = new ArrayList<>(idiomMap.keySet());

            for(Map.Entry<String, List<String>> entry : idiomMap.entrySet()) {

                List<String> list = pyingTree.get(entry.getValue().get(0));
                if(CollectionUtils.isEmpty(list)) {
                    list = new ArrayList<>();
                    list.add(entry.getKey());

                    pyingTree.put(entry.getValue().get(0), list);
                    continue;
                }

                list.add(entry.getKey());
                pyingTree.put(entry.getValue().get(0), list);
            }

        } catch (Exception e) {
            log.error("加载成语文件出现错误 {} {}", e.getCause(), e.getStackTrace());
        }
    }

    //注意要清除  uuidIdiomMap   防止撑爆，使用LRU和完成成语接龙的方法删除数据。
    //如果不及时删除uuidIdiomMap 肯定会发生堆栈溢出，内存爆掉。
    public WebResult idiomReq(String words, String uuid) {
        log.info("--开始成语接龙 " + words +"  uuid: "+ uuid);
        if(StringUtils.isBlank(words)) {
            return WebResult.getFailWebResult("-1", "--请传入question字段的值，否则无法继续。");
        }

        IdiomEntity resultIdiomEntity = new IdiomEntity();
        //开始成语接龙
        if("成语接龙".equals(words)) {
            String sysRandomIdiomWords = idiomWordsList.get((int) (Math.random() * (idiomWordsList.size() - 1)));
            log.info("--系统给出的成语 " + sysRandomIdiomWords +"  uuid: "+ uuid);

            List<IdiomEntity> idiomHitoryEntityList = new ArrayList<>();
            resultIdiomEntity = addIdiomUuidMap(uuid, sysRandomIdiomWords, idiomHitoryEntityList);
            int totalCount = idiomHitoryEntityList.size();
            resultIdiomEntity.setTotalCount(totalCount);

            //封装返回的数据
            String renderTts = "成语接龙开始啦，接龙成语要首尾同音，不想玩了随时和我说不玩了。接受挑战吧 "+resultIdiomEntity.getWords()+"，开始吧，皮卡丘！";
            String isBingo="true";
            String message = "success";
            IdiomOutputDTO dto = transDTO(resultIdiomEntity, words, isBingo, renderTts, message);

            return WebResult.getSuccWebResult(dto);
        }

        //解决非法的uuid传入问题
        if(uuidIdiomMap.get(uuid) == null) {
            return WebResult.getFailWebResult("-1", "您好，请您先进入成语接龙，然后再说成语哦。");
        }

        //换一个
        if("换一个".equals(words)) {
            //删除最近的那个，然后再放入，把最近的那个返回给用户。
            resultIdiomEntity = idiomChangeWords(uuid);
            resultIdiomEntity.setTotalCount(uuidIdiomMap.get(uuid).size());

            String renderTts = "那好啦，我们换一个，这次我出的成语是。"+resultIdiomEntity.getWords()+"，您继续吧。";
            String isBingo="true";
            String message = "success";
            IdiomOutputDTO dto = transDTO(resultIdiomEntity,words, isBingo, renderTts, message);

            return WebResult.getSuccWebResult(dto);
        }
        //不玩了
        if("不玩了".equals(words)) {
            resultIdiomEntity.setTotalCount(uuidIdiomMap.get(uuid).size());

            int count = resultIdiomEntity.getTotalCount() >= 10 ? 99 : (resultIdiomEntity.getTotalCount()-1)*10;
            String renderTts = "游戏结束啦！看看你的成绩吧。你共进行了"+resultIdiomEntity.getTotalCount()+"次接龙，打败了全国 "+count+"% 的人。想玩成语接龙，随时找我哦。";
            String isBingo="true";
            String message = "success";

            IdiomOutputDTO dto = transDTO(resultIdiomEntity,words, isBingo, renderTts, message);

            endIdiomChainMatch(uuid);
            return WebResult.getSuccWebResult(dto);
        }

        //开始进行成语接龙
        if(idiomMap != null && idiomMap.get(words) == null) {
            return WebResult.getSuccWebResult("这个好像不是成语哦。成语的第一个字必须与我说的最后一个字读音相同才行。不要放弃，再试试看。");
        }
        resultIdiomEntity = idiomChainMatch(words, uuid);
        resultIdiomEntity.setTotalCount(uuidIdiomMap.get(uuid).size());

        String renderTts = "该我了，我的回答是 "+resultIdiomEntity.getWords()+" ，下面是您继续了。";
        String isBingo="true";
        if(!resultIdiomEntity.isBingo()) {
            renderTts = "接龙成语要首尾同音，要按照规则来哦。成语的第一个字必须与我说的最后一个字读音相同才行。不要放弃，fighting！";
            isBingo="false";
        }
        String message = "success";
        IdiomOutputDTO dto = transDTO(resultIdiomEntity,words, isBingo, renderTts, message);

        return WebResult.getSuccWebResult(dto);
    }

    /**
     * 成语接龙中用户与系统进行对比
     * uuidIdiomMap 是某个uuid下的map集合
     * uuidIdiomMap key是uuid, value是成语map中的一个键值对
     *
     * words用户传入的成语
     * uuid当前用户的uuid，使用这个uuid取出uuidIdiomMap进行了多少局成语
     */
    public IdiomEntity idiomChainMatch(String words, String uuid) {
        IdiomEntity resultIdiomEntity = new IdiomEntity();
        //获取该uuid下进行的成语接龙历史记录
        List<IdiomEntity> idiomHitoryEntityList = uuidIdiomMap.get(uuid);
        //获取上一次的成语接龙的成语和其对应的拼音
        IdiomEntity idiomEntity = idiomHitoryEntityList.get(idiomHitoryEntityList.size() - 1);
        List<String> userPyinList = idiomMap.get(words);
        //取出系统的拼音
        List<String> sysPyinList = idiomEntity.getWordsPyin();

        //比如系统给的是 川流不息 用户回答 息息相关 。验证用户回答第一个与系统给的最后一个拼音进行比较
        if(userPyinList.get(0).equals(sysPyinList.get(sysPyinList.size() - 1))) {
            //回答正确情况
            List<String> idiomTreeList = pyingTree.get(userPyinList.get(userPyinList.size() - 1));

            String sysRandomIdiomWords;
            if(CollectionUtils.isEmpty(idiomTreeList)) {
                sysRandomIdiomWords = idiomWordsList.get((int) (Math.random() * (idiomWordsList.size() - 1)));
                log.info("--系统随机的成语 " + sysRandomIdiomWords +"  uuid: "+ uuid);
            } else {
                sysRandomIdiomWords = idiomTreeList.get((int) (Math.random() * (idiomTreeList.size() - 1)));
                log.info("--系统接龙的成语 " + sysRandomIdiomWords +"  uuid: "+ uuid);
            }
            resultIdiomEntity = addIdiomUuidMap(uuid, sysRandomIdiomWords, idiomHitoryEntityList);

        } else {
            //回答错误情况，保持现状，让他继续想
            resultIdiomEntity = idiomEntity;
            resultIdiomEntity.setBingo(false);
        }
        return resultIdiomEntity;
    }

    /**
     * 更换成语接龙
     * @param uuid 当前用户的uuid
     */
    public IdiomEntity idiomChangeWords(String uuid) {
        //获取该uuid下进行的成语接龙历史记录 idiomHitoryEntityList
        //idiomHitoryEntityList 取出最近的一个，重新生成一个新的成语 再次放入
        List<IdiomEntity> idiomHitoryEntityList = uuidIdiomMap.get(uuid);
        idiomHitoryEntityList.remove(idiomHitoryEntityList.size() - 1);
        String sysRandomIdiomWords = idiomWordsList.get((int) (Math.random() * (idiomWordsList.size() - 1)));

        IdiomEntity resultIdiomEntity = addIdiomUuidMap(uuid, sysRandomIdiomWords, idiomHitoryEntityList);

        log.info("--系统给出的成语 " + sysRandomIdiomWords +"  uuid: "+ uuid);
        return resultIdiomEntity;
    }

    /**
     *  将系统的回答放入记录中，不记录用户回答。因为用户回答只是验证对整个业务逻辑无多大作用。
     *      将用户的输入写入日志。
     * @param uuid 当前用户的uuid
     * @param sysRandomIdiomWords uuid下的成语
     * @param idiomHitoryEntityList 对应uuid的历史局数
     */
    private IdiomEntity addIdiomUuidMap(String uuid, String sysRandomIdiomWords, List<IdiomEntity> idiomHitoryEntityList) {
        //把给出的成语放在 uuidIdiomMap 中
        IdiomEntity newIdiomEntity = new IdiomEntity();
        newIdiomEntity.setWords(sysRandomIdiomWords);
        newIdiomEntity.setWordsPyin(idiomMap.get(sysRandomIdiomWords));
        newIdiomEntity.setCurrentTime(System.currentTimeMillis());
        newIdiomEntity.setUuid(uuid);

        idiomHitoryEntityList.add(newIdiomEntity);

        uuidIdiomMap.put(uuid, idiomHitoryEntityList);
        return newIdiomEntity;
    }

    /**
     * 成语接龙不玩了，通过不玩了 删除掉
     * @param uuid
     * @return
     */
    private IdiomEntity endIdiomChainMatch(String uuid) {
        List<IdiomEntity> idiomHitoryEntityList = uuidIdiomMap.get(uuid);
        //获取上一次的成语接龙的成语和其对应的拼音
        IdiomEntity idiomEntity = idiomHitoryEntityList.get(idiomHitoryEntityList.size() - 1);
        uuidIdiomMap.remove(uuid);
        return idiomEntity;
    }

    /**
     * 删除 uuidIdiomMap 僵尸的uuid
     * 删除规则 历史记录长度为3的 超过5分钟的进行删除
     * 该方法动态调用，10分钟调用一次。
     * 超过30分钟的成语接龙必须要删掉
     */
    @Scheduled(cron="* 0/10 * * * ?")
    private void removeZombieUuid() {
        for(Map.Entry<String, List<IdiomEntity>> entry : uuidIdiomMap.entrySet()) {
            long time = System.currentTimeMillis() - entry.getValue().get(entry.getValue().size() - 1).getCurrentTime();
            if(time >= 1000*60*5 && entry.getValue().size() <= 3) {
                uuidIdiomMap.remove(entry.getKey());
                log.info("当前uuidIdiomMap活跃 {} 多少用户，清除 uuidIdiomMap 中的僵尸数据 {}", uuidIdiomMap.size(), entry.getKey());
            }
            if(time > 1000*60*30) {
                uuidIdiomMap.remove(entry.getKey());
                log.info("当前uuidIdiomMap活跃 {} 多少用户，清除 uuidIdiomMap 中的僵尸数据 {}", uuidIdiomMap.size(), entry.getKey());
            }
        }
    }

    private IdiomOutputDTO transDTO(IdiomEntity idiomEntity, String words, String isBingo, String renderTts, String message) {
        IdiomOutputDTO dto = new IdiomOutputDTO();
        dto.setWordVal(idiomEntity.getWords());
        dto.setUuid(idiomEntity.getUuid());

        int count = idiomEntity.getTotalCount() >= 10 ? 99 : (idiomEntity.getTotalCount()-1)*10;
        dto.setRawText(words);
        dto.setRenderTts(renderTts);
        dto.setIsBingo(isBingo);
        dto.setPercentVal(String.valueOf(count));
        dto.setWordNumVal(String.valueOf(idiomEntity.getTotalCount()));
        if(StringUtils.isNotBlank(message)) {
            dto.setMessage(message);
        }

        return dto;
    }
    
}
